<?php
/**
 * Acitve Record查询类
 * 
 * 可以由q()函数快速地获取一个查询实例
 * <code>
 * $query = q("TUser");
 * $query->findAll();
 * </code>
 *
 * @since 1.0
 * @package if
 * @subpackage module.activerecord
 */
class IDbPartitionQuery extends IObject {
	private $scopes = array();
	
	/**
	 * Enter description here...
	 *
	 * @var IDbPartitionDAO
	 */
	private $dao;
	/**
	 * Enter description here...
	 *
	 * @var IDbDriver
	 */
	private $driver;
	
	private $table;
	
	/**
	 * 模型名称
	 *
	 * @var string
	 */
	private $type;
	private $scope = array();
	private $size;
	private $offset;
	private $joins = array();
	private $groups = array();
	private $having = array();
	private $params = array();
	private $folder = true;
	private $noPk = false;
	private $seperator = "\$";
	private $joinSeparator = "&";
	private $queryType = "select";
	private $tblAlias = array();
	private $resultAttrs = array();
	private $defcond = true;
	private $updatingAttrs  =array();
	private $orders = array();
	private $parsedResultAttrs = array();
	private $relations = array();
	private $combine = array();
	private $cache = array();
	private $flush = array();
	private $nativeSql;
	private $joinTypes = array();
	private $hasMany = array();
	private $tmpAttrs = array();
	private $assert = array();
	private $joinConds = array();
	private $joinOrders = array();
	private $tmpContext;
	private $noResultAttrs = array();
	const MATCH_MODE_ANYWHERE=1;
	const MATCH_MODE_START=2;
	const MATCH_MODE_END=3;
	const MATCH_MODE_EXACT=4;	
	/**
	 * 构造
	 *
	 * @param string $record 记录类型
	 * @since 1.0
	 */
	function __construct($record, $table) {
		$this->table = $table;
		$this->dao = IDbPartitionDAO::dao($record, $table);
		$this->driver = $this->dao->driver();
		$this->type = $this->dao->type();
		$this->_createScope("and");
	}
	
	private function _createScope($type) {
		unset($this->scope);
		$this->scope = array(
			"scopeType" => $type,
			"pks" => array(),
			"attrs" => array(),
			"conds" => array()
		);
		$this->scopes[] = &$this->scope;
	}
	
	/**
	 * 选择数据库
	 * 
	 * 只有在构造后立即使用此方法才能修改成功
	 *
	 * @param string $db 新的数据库配置名
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function db($db) {
		$this->driver = IDb::driverByOpt($db);
		return $this;
	}
	
	/**
	 * 设置要查询的主键值
	 * 
	 * - pk(1)
	 * - pk(1, 2, array(3, 4))
	 *
	 * @param array|scalar $pkValue 主键值
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function pk($pkValue) {
		$args = func_get_args();
		foreach ($args as $arg) {
			if (is_array($arg)) {
				$this->scope["pks"] = if_array_join($this->scope["pks"], $arg);
			}
			else {
				$this->scope["pks"][] = $arg;
			}
		}
		$this->scope["pks"] = array_unique($this->scope["pks"]);
		return $this;
	}	
	
	/**
	 * 设置排序条件
	 * 
	 * - order("id", "desc")
	 * - order(array(
	 * 		array("id", "desc"),
	 *      array("submit_date", "asc")
	 * ));
	 *
	 * @param string|array $order 排序属性
	 * @param string $type 排序类型,ASC/DESC 
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function order($order, $type = null) {
		if (is_array($order)) {
			foreach ($order as $row) {
				$_order = isset($row[0]) ? $row[0] : "";
				$_type = isset($row[1]) ? $row[1] : null;
				$this->order($_order, $_type);
			}
		}
		else {
			$order = $this->_fullField($order);
			$this->orders[] = array($order, $type);
		}
		return $this;
	}	
	
	/**
	 * 设置一个正排序
	 *
	 * @param string $attr 排序字段，从1.0.3开始，默认为主键名
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function asc($attr = null) {
		if (!$attr) {
			$attr = $this->dao->pkName();
		}
		$this->order($attr, "ASC");
		return $this;
	}
	
	/**
	 * 设置一个倒排序
	 * 
	 * @param string $attr 排序字段，从1.0.3开始，默认为主键名
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function desc($attr = null) {
		if (!$attr) {
			$attr = $this->dao->pkName();
		}
		$this->order($attr, "DESC");
		return $this;
	}	
	
	/**
	 * 设置属性查询条件
	 * 
	 * $name可以是：
	 * - attr
	 * - table.attr
	 * - array("attr" => "value");
	 * 
	 * 如果$value为空数组，则在查询中将不会加入此条件，如
	 * - $query->attr("id", $ids);
	 * 中的$ids如果为空数组，就不会加入任何条件。这可能会给你带来一个错觉，认为已经给id设定条件，但没有生效。
	 * 如果你想在ids为空的时候查询出一个空的数据，就需要自行判断，如：
	 * <code>
	 * if (empty($ids)) {
	 * 	return array();
	 * }
	 * else {
	 * 	return $query->attr("id", $ids)->findAll();
	 * }
	 * </code>
	 *
	 * @param string|array $name 属性名或属性名值对
	 * @param scalar $value 属性值
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function attr($name, $value = null) {
		if (is_array($name)) {
			foreach ($name as $_name => $_value) {
				$this->attr($_name, $_value);
			}
		}
		else {
			if (is_null($value) && preg_match("/[!=><]/", $name)) {//v1.0.3: attr("a=b") => cond("a=b")以减少错误，v1.1.2加入不等于号
				$this->cond($name);
			}
			else {
				$this->scope["attrs"][$name] = $value;
			}
		}
		return $this;
	}	
	
	/**
	 * Enter description here...
	 *
	 * @return IDbPartitionQuery
	 */
	function or_() {
		$this->_createScope("or");
		return $this;
	}
	
	/**
	 * Enter description here...
	 *
	 * @return IDbPartitionQuery
	 */
	function _or() {
		$this->_createScope("or_end");
		$this->_createScope("and");
		return $this;
	}
	
	function delim() {
		if (in_array($this->scope["scopeType"], array("or", "or_delim"))) {
			$this->_createScope("or_delim");
		}
		return $this;
	}
	
	/**
	 * 设置条件
	 *
	 * @param array|string $cond 条件
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function cond($cond) {
		if (is_array($cond)) {
			$this->scope["conds"] = if_array_join($this->scope["conds"], $cond, true);
		}
		else if (trim($cond) != "") {
			$this->scope["conds"][] = $cond;
		}
		return $this;
	}
	
	/**
	 * 设置查询返回的条数
	 * 
	 * 和limit是同义词
	 *
	 * @param integer $size 条数
	 * @return IDbPartitionQuery
	 * @since 1.0
	 * @see limit
	 */
	function size($size) {
		$size = intval($size);
		if ($size <= 0) {
			throw new IActiveRecordException("size must be great than 0", 0x100059);
		}
		$this->size = $size;
		return $this;
	}	
	
	/**
	 * 设置查询返回的条数
	 * 
	 * 和size($size);是同义词
	 *
	 * @param integer $size 条数
	 * @return IDbPartitionQuery
	 * @since 1.0
	 * @see size
	 */
	function limit($size) {
		return $this->size($size);
	}

	/**
	 * 设置记录开始的偏移量
	 * 
	 * 只有和size配合才有作用
	 *
	 * @param integer $offset 偏移量
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function offset($offset) {
		$offset = intval($offset);
		if ($offset < 0) {
			throw new IActiveRecordException("offset can not be lower than 0", 0x100060);
		}
		$this->offset = $offset;
		return $this;
	}
	
	/**
	 * 设置查询的group条件
	 *
	 * @param string|array $attr 一个或多个属性
	 * @param string $order 排序
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function group($attr, $order = null) {
		if (is_array($attr)) {
			foreach ($attr as $row) {
				if (is_array($row)) {
					$_attr = array_key_exists(0, $row) ? $row[0] : null;
					$_order = array_key_exists(1, $row) ? $row[1] : null;
					$this->group($_attr, $_order);
				}
			}
		}
		else {
			$attr = $this->_fullField($attr);
			$this->groups[] = array($attr, $order);
		}
		return $this;
	}	
	
	/**
	 * 设置查询的having条件
	 *
	 * @param string|array $cond 条件
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function having($cond) {
		if (is_array($cond)) {
			$this->havings = if_array_join($this->havings, $cond, true);
		}
		else {
			$this->havings[] = $cond;
		}
		return $this;
	}	
	
	/**
	 * 设置返回的属性
	 * 
	 * - $query->result("self.*");
	 * - $query->result("self.*,relation_table.*"); 
	 * 
	 * 从v1.0.3开始起，可以在result()方法中加入一些带有逗号的返回：
	 * - $query->result("concat('my name is'\,self.name),self.age");
	 * 注意其中的"\,"，因为逗号是字段分隔符，所以这里要被转义
	 * 
	 * 由于在v1.0.3模型可以加入composite属性，所以可以在result()方法中加入composite中定义的新属性。
	 * 对于:<br/>
	 * <code>
	 * class TblLog extends IDbPartitionRecord {
	 * 	static $_composite = array(
	 * 		"main" => array( "id", "title", "submit_date" )
	 *  );
	 * }
	 * </code>
	 * 可以使用：<br/>
	 * <code>
	 * q("TblLog")->result("self.main")->find();
	 * </code>
	 * 来查询id,title,submit_date三个字段的值
	 *
	 * @param string|array $attr1 属性1
	 * @param string|array $attr2 属性2
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function result($attr1 = null, $attr2 = null) {
		//initialize
		$this->resultAttrs = array(
			"self" => array()
		);
		
		$args = func_get_args();
		foreach ($args as $arg) {
			//对子查询的支持
			if (if_is_instance_of($arg, __CLASS__)) {
				$attr = trim($arg->asSql());
				$this->resultAttrs["self"][] = "(" . $attr . ")";
				$this->parsedResultAttrs[] = "(" . $attr . ")";
				continue;
			}
			$arg = trim($arg);
			$attrs = array();
			if (is_string($arg) && $arg != "") {
				$attrs = preg_split("/\s*(?<!\\\),\s*/", $arg);
				foreach ($attrs as $tmpKey => $attr) {
					$attrs[$tmpKey] = str_replace("\,", ",", $attr);
				}
			}
			else if (is_array($arg)) {
				$attrs = $arg;
			}
			foreach ($attrs as $attr) {
				$attr = trim($attr);
				if ($attr != "") {
					if (!preg_match("/^(" . preg_quote($this->joinSeparator) . ")?([a-z0-9_]+|\*)(\s*\.\s*([a-z0-9_]+|\*))*$/i", $attr)) {
						if ($attr{0} == "^") { //v1.0.3: 有一些字段不需要取出来，需要在字段前加上^号
							$this->noResultAttrs[] = substr($attr, 1);
						}
						else {
							$this->resultAttrs["self"][] = $attr;
							$this->parsedResultAttrs[] = preg_replace_callback("/(" . preg_quote($this->joinSeparator) . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($this, "_parseAttr"), $attr);		}
						continue;
					}
					if (!strstr($attr, ".")) {//单个字段
						$attr = "self." . $attr;
					}
					$this->_parseResultAttr($attr);
				}
			}
		}
		
		return $this;
	}
	
	/**
	 * 设置参数
	 *
	 * @param string|array $name 参数或者参数对
	 * @param mixed $value 参数值，可以为null，标量或者IDbPartitionQuery对象
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function param($name, $value = null) {
		if (is_array($name)) {
			foreach ($name as $attr => $value) {
				$this->param($attr, $value);
			}
		}
		else if (if_is_instance_of($value, __CLASS__)) {
			$this->params[$name] = new IDbExpression($value->asSql());
		}
		else {
			$this->params[$name] = $value;
		}
		return $this;
	}
	
	/**
	 * 是否将hasMany对应的记录合成一个数组
	 *
	 * @param boolean $folder 是否合成一个数组
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function folder($folder = true) {
		$this->folder = $folder;
		return $this;
	}	
	
	/**
	 * 将当前对象转换成SQL字符串
	 *
	 * @return string
	 * @since 1.0
	 */	
	function asSql() {
		$query = $this->_clone();
		
		//如果查询原生的SQL
		if ($query->queryType == "sql") {
			$sql = $query->nativeSql;
			$sql = preg_replace_callback("/\btable(\.(\w+))+\b/s", array($query, "_parseTable"), $sql);
			$sql = preg_replace_callback("/(" . preg_quote($query->joinSeparator) . ")?\b([a-z0-9_]+)(\s*\.\s*([a-z0-9_]+\b|\*))+/is", array($query, "_parseAttr"), $sql);
			
			return $sql;
		}
		
		//结束delim
		$query->_createScope("and");
		
		$sql = "";
		$nil = nil;
		$joinHolder = "<join{$nil}></join>";
		if (in_array($query->queryType, array("count", "select", "max", "min", "sum", "avg"))) {//count,select
			$sql .= "SELECT \n";		
		
			//defcond
			if ($query->defcond) {
				$query->cond($query->dao->conds());
			}
		
			//resultAttrs 
			$sql .= "<result{$nil}></result>";
			$sql .= "FROM " . $query->_qKey($query->dao->table()) . " AS " . $query->_alias($query->type) . " \n";
			$sql .= $joinHolder;	
		}
		else if ($query->queryType == "delete") {//delete
			$sql .= "DELETE ";	
			$sql .= "FROM " . $query->_qKey($query->dao->table()) . " \n";
			$sql .= $joinHolder;	
		}
		else if ($query->queryType == "update") {//update
			$sql .= "UPDATE \n";	
			$sql .= $query->_qKey($query->dao->table()) . " \n";
			$sql .= "<join{$nil}></join>";
			$sql .= "SET \n";
			$columns = array();
			foreach ($query->updatingAttrs as $attr=>$value) {
				$columns[] = "{$attr}=" . $query->driver->quote($value);
			}
			$sql .= implode(",", $columns) . " \n";
		}
		
		//where
		$sql .= "<where{$nil}></where>\n";
		
		//group
		if (!empty($query->groups)) {
			$groups = array();
			foreach ($query->groups as $group) {
				$groups[] = implode(" ", $group);
			}
			$sql .= " GROUP BY " . implode(",", $groups) . " \n";
		}
		
		//having
		if (!empty($query->havings)) {
			$sql .= " HAVING (" . implode(") AND (", $query->havings) . ") \n";
		} 
		
		//order
		$orders = array();
		if ($query->queryType == "select") {
			if (!empty($query->orders)) {
				foreach ($query->orders as $order) {
					if (is_array($order) && !empty($order)) {
						if (if_is_instance_of($order[0], "IDbExpression")) {
							$order[0] = $order[0]->value();
						}
						elseif (!strstr($order[0], ".")) {
							$order[0] = "self." . trim($order[0]);
						}
						$orders[] = implode(" ", $order);
					}
					else {
						$orders[] = $order;
					}
				}
			}
			$sql .= "<order{$nil}></order>";
		}		
		
		//offset|size
		if ($query->queryType != "count" && $query->offset >= 0 && $query->size > 0) {
			$sql = $query->driver->limit($sql, $query->size, $query->offset);
		}
		
		//处理表间关联
		$sql = preg_replace_callback("/(" . $query->joinSeparator . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($query, "_parseAttr"), $sql);
		
		//处理result	
		if (empty($query->parsedResultAttrs)) {
			$query->_parseResultAttr("self.*");
		}
		if (!in_array($query->queryType, array("count", "sum", "max", "min", "avg"))) {
			//如果是查询字段值，则当前的ID一定要取出来
			if (!empty($query->parsedResultAttrs) && !$query->noPk) {
				$query->_parseResultAttr("self." . $query->dao->pkName());
			}	
		}
		
		//parse result
		$query->_addRelationResults();
		if (!empty($query->noResultAttrs)) {//v1.0.3: 去除已经设定不取出的
			$parsedNoResultAttrs = array();
			foreach ($query->noResultAttrs as $v) {
				$parsedNoResultAttrs[] = $query->_parseResultAttr($v);
			}
			$query->parsedResultAttrs = array_diff($query->parsedResultAttrs, $parsedNoResultAttrs);
 		}
		$sql = str_replace("<result{$nil}></result>", implode(",\n", array_unique($query->parsedResultAttrs)) . " \n", $sql);

		//加入where	
		$where = $query->_where();
		if ($where) {
			$where ="WHERE\n " . strtr($where, $query->tmpAttrs) . "\n";
		}
		
		//join加入条件
		if (!empty($query->joinConds)) {
			$joinCond = preg_replace_callback("/(" . $query->joinSeparator . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($query, "_parseAttr"), "(" . implode(") AND (", $query->joinConds)  . ")");
			$where = $where ? $where . " AND {$joinCond}" : "WHERE\n " . $joinCond;
		}
		$sql = str_replace("<where{$nil}></where>",  $where, $sql);
		
		//order
		if ($query->queryType == "select") {
			$orderBy = empty($orders) ? "" : "ORDER BY " . implode(",", $orders);
			if ($orderBy) {
				$orderBy = preg_replace_callback("/(" . $query->joinSeparator . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($query, "_parseAttr"), $orderBy);
			}
			if (!empty($query->joinOrders)) {
				$joinOrder = preg_replace_callback("/(" . $query->joinSeparator . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($query, "_parseAttr"), implode(",", $query->joinOrders));
				$orderBy = $orderBy ? $orderBy . ",{$joinOrder}" : "ORDER BY {$joinOrder}";
			}
			$sql = str_replace("<order{$nil}></order>", $orderBy ? "\n" . $orderBy . "\n" : "", $sql);
		}
		
		//加入join
		$joinSql = array();
		foreach ($query->joins as $attr => $join) {
			if (isset($query->joinTypes[$attr])) {
				$join["type"] = $query->joinTypes[$attr];
			}
			$joinSql[] = $join["type"] . " JOIN " . $join["table"] . " ON " . $join["on"];
		}
		$sql = str_replace($joinHolder, empty($joinSql) ? "" : implode(" \n", $joinSql) . " \n", $sql);		

		return $sql;
	}
	
	private function _addRelationResults() {
		if (in_array($this->queryType, array("count", "sum", "max", "min", "avg"))) { 
			return;
		}
		foreach ($this->resultAttrs as $alias => $attrs) {
			if ($alias == "self") {
				continue;
			}
			$parent = substr($alias, 0, strrpos($alias, "."));
			if ($parent == "") {
				$parent = "self";
			}
			$relation = $this->joins[$alias]["relation"];
			if (!isset($this->resultAttrs[$parent]) || !in_array($relation["fk"], $this->resultAttrs[$parent])) {
				$this->_parseResultAttr($parent . "." . $relation["fk"]);
				$this->_addRelationResults();
			}
			if (!isset($this->resultAttrs[$alias]) || !in_array($relation["by"], $this->resultAttrs[$alias])) {
				$this->_parseResultAttr($alias . "." . $relation["by"]);
				$this->_addRelationResults();
			}
			if ($relation["type"] == IDbPartitionRecord::HAS_AND_BELONGS_TO_MANY) {
				$this->parsedResultAttrs[] = $this->_alias($relation["joinTable"], $this->joinSeparator . $alias) . "." . $this->_qKey($relation["joinFk"]) . " AS " . $this->_qKey($this->joinSeparator . $alias . $this->seperator . $relation["joinFk"]);
				$this->parsedResultAttrs[] = $this->_alias($relation["joinTable"], $this->joinSeparator . $alias) . "." . $this->_qKey($relation["joinBy"]) . " AS " . $this->_qKey($this->joinSeparator . $alias . $this->seperator . $relation["joinBy"]);
			}
			
			//add pk
			if (in_array($relation["type"], array(IDbPartitionRecord::HAS_AND_BELONGS_TO_MANY, IDbPartitionRecord::HAS_MANY))) {
				$pkName = IDbPartitionDAO::dao($relation["target"], $this->table)->pkName();
				$this->parsedResultAttrs[] = $this->_alias($relation["target"], $alias) . "." . $this->_qKey($pkName) . " AS " . $this->_qKey($alias . $this->seperator . $pkName);
			}
		}
	}
	
	/**
	 * 分析条件
	 *
	 * @return string
	 * @since 1.0.2
	 */
	private function _where() {
		//构造ands
		$ands = array();
		$ors = array();
		foreach ($this->scopes as $scope) {
			unset($this->scope);
			$this->scope = $scope;
			$wheres = array();
			if (!empty($this->scope["pks"])) {
				$this->attr("self." . $this->dao->pkName(), $this->scope["pks"]);
			}
			
			//属性值
			foreach ($this->scope["attrs"] as $attr => $value) {
				$attr = $this->_fullField($attr);
				$tmpKey = uniqid("$$$");
				if (is_array($value) && !empty($value)) {
					if (count($value) == 1) {
						$this->tmpAttrs[$tmpKey] = $this->_qData(array_shift($value));
						$this->cond($attr . "=" . $tmpKey);
					}
					else {
						$this->tmpAttrs[$tmpKey] = " IN (" . implode(",", $this->_qData($value)) . ")";
						$this->cond($attr . $tmpKey);
					}
				}
				else if (is_scalar($value) || is_null($value) || if_is_instance_of($value, "IDbExpression")) {
					$this->tmpAttrs[$tmpKey] = $this->_qData($value);
					$this->cond($attr . "=" . $tmpKey);
				}
			}
			$wheres = array_unique($this->scope["conds"]);
			if ($this->scope["scopeType"] == "and") {
				//end or delim
				if (!empty($ors)) {
					$ands[] = "(" . implode(" OR ", $ors) . ")";
					$ors = array();
				}
				
				if (!empty($wheres)) {
					$ands[] = "(" . implode(")\n AND (", $wheres) . ")";
				}
			}
			else if (in_array($this->scope["scopeType"], array("or", "or_delim"))) {//or处理
				if (!empty($wheres)) {
					$ors[] = "(" . implode(") AND (", $wheres) . ")";
				}
			}
		}
		$where = trim(implode(" AND ", $ands));
		$where = preg_replace_callback("/(" . $this->joinSeparator . ")?\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($this, "_parseAttr"), $where);
		return $where;
	}
	
	/**
	 * 处理记录属性的回调函数
	 *
	 * @param array $match 正则匹配结果
	 * @return string
	 * @since 1.0
	 */
	private function _parseAttr($match) {
		$array = $this->_parseAttrJoin($match);
		if (empty($array)) {
			return $match[0];
		}
		extract($array);
		return $qAlias . "." . (($attr == "*") ? "*" : $this->_qKey($attr));
	}
	
	/**
	 * 处理返回字段的回调函数
	 *
	 * @param array|string $match 正则匹配结果
	 * @return string|null
	 * @since 1.0
	 */
	private function _parseResultAttr($match) {
		$array = $this->_parseAttrJoin($match);
		if (empty($array)) {
			return $match[0];
		}
		extract($array);
		if (!isset($this->resultAttrs[$alias])) {
			$this->resultAttrs[$alias] = array();
		}
		
		$target = ($alias == "self") ? $this->type : $this->joins[$alias]["relation"]["target"];
		$dao = IDbPartitionDAO::dao($target, $this->table);
		
		if($attr != "*") {
			//是否在composite中，在v1.0.3中加入
			$composite = $dao->composite($attr);
			if ($composite && is_array($composite)) {
				foreach ($composite as $realAttr) {
					$this->result($alias . "." . $realAttr);
				}
				return;
			}
			
			$this->resultAttrs[$alias][] = $attr;
			$parsedResult = $qAlias . "." . $this->_qKey($attr) . " AS " . $this->_qKey($alias . $this->seperator . $attr);
			$this->parsedResultAttrs[] = $parsedResult;
			return $parsedResult;
		}
		foreach($dao->columns() as $column) {
			$this->resultAttrs[$alias][] = $column;
			$this->parsedResultAttrs[] = $qAlias . "." . $this->_qKey($column) . " AS " . $this->_qKey($alias . $this->seperator . $column);
		}
	}	
	
	private function _parseAttrJoin($match) {
		if (!is_array($match)) {
			$match = array($match);
		}
		$field = $match[0];
		$isJoin = false;
		if (preg_match("/^" . preg_quote($this->joinSeparator) . "(.+)$/", $field, $match2)) {
			$isJoin = true;
			$field = $match2[1];
		}
		$lastPos = strrpos($field, ".");
		$alias = substr($field, 0, $lastPos);
		$attr = substr($field, $lastPos + 1);
		$qAlias = null;
		if (isset($this->tblAlias[$alias])) {
			$qAlias = $this->_qKey($this->tblAlias[$alias]);
		}
		else {
			$pieces = explode(".", $field);
			array_walk($pieces, create_function('&$value', '$value=trim($value);'));
			array_pop($pieces);
			
			if (!empty($pieces) && $pieces[0] == "self") {
				$qAlias = $this->_alias($this->type);
			}
			else {
				$lastDAO = IDbPartitionDAO::dao($this->type, $this->table);
				foreach ($pieces as $key => $name) {
					$relation = $lastDAO->relation($name);
					if (!$relation) {
						//throw new IActiveRecordException("no relation named '{$name}' defined in " . $lastDAO->type());
						return array();
					}
					if (in_array($relation["type"], array(IDbPartitionRecord::HAS_MANY, IDbPartitionRecord::HAS_AND_BELONGS_TO_MANY))) {
						$this->hasMany[] = $relation;
					}
					$currentDAO = IDbPartitionDAO::dao($relation["target"], $this->table);
					$_attr = implode(".", array_slice($pieces, 0, $key + 1));
					$lastAttr = substr($_attr, 0, strrpos($_attr, "."));
					if ($lastAttr == "") {
						$lastAttr = "self";
					}
					if (!isset($this->joins[$_attr])) {
						if ($relation["type"] != IDbPartitionRecord::HAS_AND_BELONGS_TO_MANY) {
							$this->joins[$_attr] = array(
								"type" => "LEFT",
								"relation" => $relation,
								"attr" => $_attr,
								"on" => $this->_alias($lastDAO->type(), $lastAttr) . "." . $this->_qKey($relation["fk"]) . "=" . $this->_alias($relation["target"], $_attr) . "." . $this->_qKey($relation["by"]),
								"table" => $this->_qKey($currentDAO->table()) . " AS " . $this->_alias($relation["target"], $_attr)
							);
						}
						else {
							$joinAttr = $this->joinSeparator . $_attr;
							$this->joins[$joinAttr] = array(
								"type" => "LEFT",
								"relation" => $relation,
								"attr" => $joinAttr,
								"on" => $this->_alias($lastDAO->type(), $lastAttr) . "." . $this->_qKey($relation["fk"]) . "=" . $this->_alias($relation["joinTable"], $joinAttr) . "." . $this->_qKey($relation["joinFk"]),
								"table" => $this->_qKey(IDbPartitionDAO::dao($relation["joinTable"], $this->table)->table()) . " AS " . $this->_alias($relation["target"], $joinAttr) 
							);
							$this->joins[$_attr] = array(
								"type" => "LEFT",
								"relation" => $relation,
								"attr" => $_attr,
								"on" => $this->_alias($relation["joinTable"], $joinAttr) . "." . $this->_qKey($relation["joinBy"]) . "=" . $this->_alias($currentDAO->type(), $_attr) . "." . $this->_qKey($relation["by"]),
								"table" => $this->_qKey($currentDAO->table()) . " AS " . $this->_alias($relation["target"], $_attr) 
							);
						}
						//处理cond和order
						$this->tmpContext = substr($_attr, 0, strrpos($_attr, "."));
						if (!empty($relation["cond"])) {
							$this->joinConds[] = preg_replace_callback("/\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($this, "_parseDefCond"), $relation["cond"]);
						}
						if (!empty($relation["order"])) {//v1.0.3 修复了在order中有多级表的问题
							$this->joinOrders[] = preg_replace_callback("/\b([a-z0-9_]+)(\s*\.\s*(([a-z0-9_]+)\b|\*))+/is", array($this, "_parseDefCond"), $relation["order"]);
						}
					}
					$lastDAO = $currentDAO;
				}
				$qAlias = $this->_alias($lastDAO->type(), $alias);
			}
		}
		
		return array(
			"alias" => $isJoin ? $this->joinSeparator . $alias : $alias,
			"qAlias" => $isJoin ? $this->_alias($this->joins[$this->joinSeparator . $alias]["relation"]["joinTable"], $this->joinSeparator . $alias) : $qAlias,
			"attr" => $attr,
			"isJoin" => $isJoin
		);
	}
	
	private function _parseDefCond($match) {
		if ($match[1] == "self") {
			return $this->tmpContext ? $this->tmpContext . $match[2] : $match[0];
		}
		return $this->tmpContext ? $this->tmpContext . "." . $match[0] : $match[0];
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $match
	 * @return unknown
	 * @since 1.0
	 */
	private function _parseTable($match) {
		$target = substr($match[0], strpos($match[0], ".") + 1);;
		if ($target == "self") {
			return $this->_qKey($this->dao->table()) . " AS " . $this->_alias($this->type, "self");
		}
		$array = $this->_parseAttrJoin($target . ".x");
		if (empty($array)) {
			return $array[0];
		}
		return $this->_qKey(IDbPartitionDAO::dao($this->joins[$target]["relation"]["target"], $this->table)->table()) . " AS " . $array["qAlias"];
	}
	
	/**
	 * 取得一个class对应的别名
	 *
	 * @param string $class 类名
	 * @return string
	 * @since 1.0
	 */
	private function _alias($class, $name = "self") {
		$mark = $class . $this->seperator . $name;
		if (!isset($this->tblAlias[$name])) {
			if (!in_array($this->queryType, array("delete", "update", "insert"))) {
				$this->tblAlias[$name] =  $mark;
			}
			else {
				$this->tblAlias[$name] = IDbPartitionDAO::dao($class, $this->table)->table();
			}
		}
		return $this->_qKey($this->tblAlias[$name]);
	}
	
	/**
	 * 将当前对象转换为字符串
	 *
	 * @return string
	 * @since 1.0
	 */
	function __toString() {
		return $this->asSql();
	}	
	
	/**
	 * 是否查询出主键
	 * 
	 * 默认所有查询都要查出主键，除非有特殊的需求，否则请不要改变此行为
	 *
	 * @param boolean $noPk 是否查询出主键
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function noPk($noPk = true) {
		$this->noPk = $noPk;
		return $this;
	}	
	
	private function _qKey($key) {
		return $this->driver->quoteKeyword($key);
	}
	
	private function _qData($data) {
		if (is_array($data)) {
			foreach ($data as $key => $value) {
				$data[$key] = $this->_qData($value);
			}
			return $data;
		}
		return $this->driver->quote($data);
	}
	
	private function _fullField($field) {
		if (if_is_instance_of($field, "IDbExpression")) {
			return $field;
		}
		return strstr($field, ".") ? $field : "self.{$field}";
	}
	
	/**
	 * 是否使用默认的条件
	 *
	 * @param boolean $bool true|false
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function defcond($bool = true) {
		$this->defcond = $bool;
		return $this;
	}	
	
	/**
	 * Enter description here...
	 *
	 * @return IDbStatement
	 * @since 1.0
	 */
	function exec() {
		$stmt = $this->driver->prepare($this->asSql(), $this->params);
		$stmt->execute();
		return $stmt;
	}
	
	private function _exec($sql, $params) {
		$stmt = $this->driver->prepare($sql, $params);
		$stmt->setParseError(false);
		$stmt->execute();
		if (!$this->driver->support("offset") && $this->offset > 0) {
			$stmt->move($this->offset);
		}
		return $stmt;
	}
	
	private function _findAll() {
		$sql = $this->asSql();
		
		//清空缓存
		$this->flushCache($sql);
		
		//缓存
		$cache = $this->readCache($sql);
		if (!is_null($cache)) {
			return $cache;
		}		
		
		$allRows  = array();
		
		//是否一对多
		if (!empty($this->hasMany) && $this->size > 0 && $this->queryType == "select") {
			$query2 = clone $this;
			$sql2 = $query2->clean()->result("DISTINCT(self." . $this->dao->pkName() . ") AS tmp_id")->offset($this->offset)->size($this->size)->asSql();
			$stmt2 = $query2->_exec($sql2, $this->params);
			$ids = if_array_pick($stmt2->fetchAll(IDb::FETCH_ASSOC), "tmp_id");
			if (!empty($ids)) {
				$query3 = clone $this;
				$query3->size = -1;
				$query3->offset = -1;
				$stmt = $query3->_exec($query3->pk($ids)->asSql(), $this->params);
				$allRows = $stmt->fetchAll(IDb::FETCH_ASSOC);
			}
		}
		else {
			$stmt = $this->_exec($sql, $this->params);
			$allRows = $stmt->fetchAll(IDb::FETCH_ASSOC);
		}
		if (!$this->folder) {
			$this->writeCache($sql, $allRows);
			return $allRows;	
		}
		$regular = true;
		$ret = array();
		$pkName = "self" . $this->seperator . $this->dao->pkName();
		$attrIndexes = array();
		foreach ($allRows as $rowIndex => $row) {
			$newRow = array();
			$pk = null;
			if (isset($row[$pkName])) {
				$pk = $row[$pkName];
			}
			foreach ($row as $key => $value) {
				if (!is_null($pk)) {
					if (!strstr($key, $this->seperator)) {
						$key = "self" . $this->seperator . $key;
					}
					if (substr($key, 0, strlen($this->joinSeparator)) != $this->joinSeparator) {
						list($alias, $attr) = explode($this->seperator, $key);
						$this->_attrToArray($attrIndexes, $ret, $row, $alias, $attr, $value);
					}
				}
				else {
					$ret[$rowIndex][$key] = $value;
				}
			}
		}
		if ($regular) {
			$ret = array_values($ret);
		}
		
		//assert
		if (!empty($this->assert) && empty($ret)) {
			throw new IAssertException($this->assert["message"] ? $this->assert["message"] : "query assert failed", 0x100061);
		}
		
		$this->writeCache($sql, $ret);
		return $ret;
	}
	
	/**
	 * 查找符合条件的所有记录
	 *
	 * @param boolean $array 是否以数组形式返回
	 * @return array|IDbPartitionRecordSet
	 * @since 1.0
	 */	
	function findAll($array=true) {
		$ret = $this->_findAll();
		if ($array) {
			if (!empty($this->combine)) {
				$ret = if_array_combine($ret, $this->combine["key"], $this->combine["value"]);
				$this->combine = array();
			}
			return $ret;
		}

		$set = new IDbPartitionRecordSet($this->dao);
		$set->setRecords($ret);
		$set->setQuery($this);
		return $set;
	}
	
	private function _attrToArray(&$attrIndexes, &$ret, $row, $alias, $attr, $value) {
		$exec = array();
		$pk = $row["self" . $this->seperator . $this->dao->pkName()];
		
		$exec[] = "[" . $this->_qIndex($pk) . "]";
		if ($alias != "self") {
			$pieces = explode(".", $alias);
			foreach ($pieces as $key => $piece) {
				$exec[] = "[" . $this->_qIndex($piece) . "]";
				$_alias = implode(".", array_slice($pieces, 0, $key + 1));
				$relation = $this->joins[$_alias]["relation"];
				$parent = substr($_alias, 0, strrpos($_alias, "."));
				if ($parent == "") {
					$parent = "self";
				}
				$fkValue = $row[$parent . $this->seperator . $relation["fk"]];
				$byValue = $row[$_alias . $this->seperator . $relation["by"]];
				if (is_null($byValue)) {
					eval("\$ret" . implode("", $exec) . "=array();");
					return;
				}
				if (in_array($relation["type"], array(IDbPartitionRecord::HAS_MANY, IDbPartitionRecord::HAS_AND_BELONGS_TO_MANY))) {
					$targetFkValue = $row[$_alias  . $this->seperator . IDbPartitionDAO::dao($relation["target"], $this->table)->pkName()];
					if (!isset($attrIndexes[$_alias . "." . $fkValue])) {
						$attrIndexes[$_alias . "." . $fkValue] = array(
							"start" => $alias . "." . $attr, 
							"index" => 0,
							"by" => $targetFkValue,
							"byFks" => array($targetFkValue)
						);
					}
					else {
						$currIndexArray = $attrIndexes[$_alias . "." . $fkValue];
						if (in_array($targetFkValue, $currIndexArray["byFks"]) && $currIndexArray["byFks"][count($currIndexArray["byFks"]) - 1] != $targetFkValue) {
							return;
						}
						elseif ($alias . "." . $attr == $attrIndexes[$_alias . "." . $fkValue]["start"] && $targetFkValue != $attrIndexes[$_alias . "." . $fkValue]["by"]) {
							$attrIndexes[$_alias . "." . $fkValue]["index"] ++;
							$attrIndexes[$_alias . "." . $fkValue]["by"] = $targetFkValue;
							$attrIndexes[$_alias . "." . $fkValue]["byFks"][] = $targetFkValue; 
						}
					}
					$counter = $attrIndexes[$_alias . "." . $fkValue];
					$exec[] = "[" . $counter["index"] . "]";
				}
			}
		}
		$exec[] = "[" . $this->_qIndex($attr) . "]";
		eval("\$ret" . implode("", $exec) . "=\$value;");
	}
	
	private function _qIndex($index) {
		return var_export($index, true);
	}
	
	/**
	 * 查找单条记录
	 *
	 * @param mixed $pk 一个主键或者一批主键
	 * @param boolean $array 是否以数组形式返回
	 * @return array|IDbPartitionRecord
	 * @since 1.0
	 */
	function find($pk = nil, $array = false) {
		$all = array();
		if (!if_is_nil($pk)) {
			$all = $this->pk($pk)->size(1)->findAll($array);
		}
		else {
			$all = $this->size(1)->findAll($array);
		}
		if (!empty($all)) {
			return $array ? $all[0] : $all->record(0);
		}
		return $array ? array() : null;
	}	
	
	/**
	 * 根据客户端请求查找单条记录
	 *
	 * @param string|null $requestPkName 请求的参数名，如果为空，则默认为当前表主键名
	 * @param boolean $array 是否以数组形式返回
	 * @return array|IDbPartitionRecord
	 * @since 1.0
	 */	
	function findx($requestPkName = null, $array = false) {
		if ($requestPkName == "") {
			$requestPkName = $this->dao->pkName();
		}
		$value = x($requestPkName);
		if (is_null($value)) {
			return $this->find(nil, $array);
		}
		return $this->find($value, $array);
	}	
	
	/**
	 * 获取查询结果中某个列的值
	 *
	 * @param integer|string $column 列号
	 * @return mixed
	 * @since 1.0
	 */
	function fetch($column = 0) {
		$array = $this->_clone()->find(nil, true);
		if (is_numeric($column)) {
			$array = array_values($array);
		}
		return if_array_get($array, $column);
	}

	/**
	 * 取得当前查询用的驱动
	 *
	 * @return IDbDriver
	 * @since 1.0
	 */
	function driver() {
		return $this->driver;
	}

	/**
	 * 魔术方法，用来支持findBy<Attr>, findAllBy<Attr>和top<N>
	 * 
	 * - findBy<Attr>($attrValue);
	 * - findAllBy<Attr>($attrValue);
	 * - top<N>($size, $cond, $order, $retArray);
	 * - findBy<Attr1>And<Attr2>($attr1Value, $attr2Value); //v1.0.3
	 * - findAllBy<Attr1>And<Attr2>($attr1Value, $attr2Value); //v1.0.3
	 * - updateBy ... //v1.1.2
	 * - deleteBy ... //v1.1.2
	 * - countBy ... //v1.1.2 
	 * - avgBy ...//v1.1.2
	 * - sumBy ...//v1.1.2
	 * - fetchBy ...//v1.1.2
	 * - maxBy ...//v1.1.2
	 * - minBy ...//v1.1.2
	 * - pageBy ...//v1.1.2
	 * 
	 * 从1.0.3开始，可以根据两个或更多的字段查找
	 * 
	 * 从1.1.0开始，方法名不区分大小写(以前版本中的findBy等前缀是区分大小写的)
	 *
	 * @param string $method 方法名
	 * @param array $params 调用方法用的参数
	 * @return mixed
	 * @since 1.0
	 */
	function __call($method, $params) {
		if (preg_match("/^(find|findAll|update|delete|count|avg|sum|fetch|max|min|page)By(\w+)$/i", $method, $match)) {
			$column = if_name_to_c($match[2]);
			$attrs = explode("_and_", $column);
			foreach ($attrs as $key => $attr) {
				$this->attr($attr, isset($params[$key]) ? $params[$key] : null);	
				unset($params[$key]);
			}
			if (strtolower($match[1]) == "update" && empty($params)) {
				throw new IException("You must define more arguments for " . __CLASS__ . "::{$method}()", 0x100013);
			}
			return call_user_func_array(array($this, $match[1]), $params);
		}
		else if (preg_match("/^top(\d+)$/i", $method, $match)) {
			array_unshift($params, $match[1]);
			return call_user_func_array(array($this, "top"), $params);
		}
		
		throw new IException("Call to undefined method " . __CLASS__ . "::{$method}()", 0x100014);
	}	
	
	/**
	 * 取得查询类型
	 * 
	 * 如count ...
	 *
	 * @param string $queryType 查询类型
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	private function queryType($queryType) {
		$this->queryType = strtolower($queryType);
		return $this;
	}

	/**
	 * 查询记录数
	 *
	 * @param string $attr 属性
	 * @return integer
	 * @since 1.0
	 */
	function count($attr = null) {
		$uniqueKey = uniqid("count");
		$attr = (!$attr)?"*":$attr;
		if (preg_match("/^\w+$/", $attr)) {
			$attr = "self." . $attr;
		}
		$result = $this->_clone()->clean()->queryType("count")->result("COUNT({$attr}) AS {$uniqueKey}")->fetch($uniqueKey);
		return $result;		
	}	
	
	function clean() {
		$this->parsedResultAttrs = array();
		$this->queryType = "select";
		return $this;
	}
	
	/**
	 * 计算总和
	 * 
	 * @param string $attr 属性
	 * @param mixed $default 默认值
	 * @return float
	 * @since 1.0
	 */
	function sum($attr, $default = null) {
		$uniqueKey = uniqid("count");
		$attr = (!$attr) ? "*" : $attr;
		if (preg_match("/^\w+$/", $attr)) {
			$attr = "self." . $attr;
		}
		$result = $this->_clone()->clean()->queryType("sum")->result("SUM({$attr}) AS {$uniqueKey}")->fetch($uniqueKey);
		return !is_null($result)?$result:$default;		
	}	
	
	/**
	 * 克隆当前对象
	 * 
	 * - v1.0.3 修复克隆后仍影响先前查询的bug
	 *
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function _clone() {
		$query = clone $this;
		//current scope
		$currentScope = array_pop($query->scopes);
		unset($query->scope);
		$query->scope = $currentScope;
		$query->scopes[] = &$query->scope;
		return $query;
	}
	
	/**
	 * 计算最小值
	 * 
	 * @param string $attr 属性
	 * @param mixed $default 默认值
	 * @return float
	 * @since 1.0
	 */
	function min($attr, $default = null) {
		$uniqueKey = uniqid("min");
		$attr = (!$attr) ? "*" : $attr;
		if (preg_match("/^\w+$/", $attr)) {
			$attr = "self." . $attr;
		}
		$result = $this->_clone()->clean()->queryType("min")->result("MIN({$attr}) AS {$uniqueKey}")->fetch($uniqueKey);
		return !is_null($result)?$result:$default;		
	}		
	
	/**
	 * 计算最大值
	 * 
	 * @param string $attr 属性
	 * @param mixed $default 默认值
	 * @return float
	 * @since 1.0
	 */
	function max($attr, $default = null) {
		$uniqueKey = uniqid("max");
		$attr = (!$attr) ? "*" : $attr;
		if (preg_match("/^\w+$/", $attr)) {
			$attr = "self." . $attr;
		}
		$result = $this->_clone()->clean()->queryType("max")->result("MAX({$attr}) AS {$uniqueKey}")->fetch($uniqueKey);
		return !is_null($result) ? $result : $default;		
	}	
	
	/**
	 * 计算平均值
	 * 
	 * @param string $attr 属性
	 * @param mixed $default 默认值
	 * @return float
	 * @since 1.0
	 */
	function avg($attr, $decimal = 0, $default = null) {
		$uniqueKey = uniqid("avg");
		$attr = (!$attr) ? "*" : $attr;
		if (preg_match("/^\w+$/", $attr)) {
			$attr = "self." . $attr;
		}
		$result = $this->_clone()->clean()->queryType("avg")->result("AVG({$attr}) AS {$uniqueKey}")->fetch($uniqueKey);
		$result = !is_null($result) ? $result : $default;		
		return round($result, $decimal);
	}	
	
	/**
	 * 删除符合条件的记录
	 * 
	 * 并返回影响的行数
	 *
	 * @return integer
	 * @since 1.0
	 */
	function delete() {
		$query = $this->_clone();
		$ret = $query->driver->exec($query->queryType("delete")->asSql());
		return $ret;
	}	
	
	/**
	 * 更新符合条件的记录
	 * 
	 * 并返回影响的行数
	 * 
	 * - update(array $fields)
	 * - update($field, $value)
	 *
	 * @param array|string $attr 属性名或属性名值对
	 * @param mixed $value 属性值
	 * @return integer
	 * @since 1.0
	 */	
	function update($attr, $value = null) {
		$query = clone $this;
		if (!is_array($attr)) {
			$attr = array($attr => $value);
		}
		$query->updatingAttrs = $this->_dealAttrs($attr);
		$ret = $query->driver->exec($query->queryType("update")->asSql());
		return $ret;
	}	
	
	/**
	 * 将返回的记录集重新组织成一个键值对
	 * 
	 * 比如从
	 * <code>
	 * array(
	 * 	array("id" => 1, "name" => "iwind"),
	 *  array("id" => 2, "name" => "kiki")
	 * );
	 * </code>
	 * 经
	 * <code>
	 * $query->combine("id", "name")->findAll();
	 * </code>
	 * 变成
	 * <code>
	 * array(
	 * 	1 => "iwind",
	 *  2 => "kiki"
	 * )
	 * </code>
	 *
	 * @param string $key 键的名称
	 * @param string|null $value 值得名称
	 * @return IDbPartitionQuery
	 */
	function combine($key, $value = null) {
		$this->combine = array("key" => $key, "value" => $value);
		return $this;
	}	
	
	/**
	 * 查询分页
	 *
	 * @param IPageStyle $pager 分页样式
	 * @param boolean $array 是否以数组形式返回
	 * @return IPageStyle
	 * @since 1.0
	 */
	function queryPage(IPageStyle $pager, $array = true) {
		$query = $this->_clone();
		if (empty($query->hasMany)) {
			$query->_where();
		}
		if (!empty($query->hasMany)) {
			$pkName = $this->dao->pkName();
			$count = $query->count("DISTINCT(self." . $pkName  . ")");
			if ($count == 0) {
				return $pager;
			}
			$pager->setTotal($count);
			$pager->setRows($query->_clone()->offset($pager->offset())->size($pager->size())->findAll($array));
			return $pager;
		}
		else {
			$pager->setTotal($query->count());
			$pager->setRows($query->offset($pager->offset())->size($pager->size())->findAll($array));
		}
		return $pager;
	}	
	
	/**
	 * 设置缓存策略
	 *
	 * @param string $engine 缓存引擎：file,apc
	 * @param integer $ttl 刷新时间
	 * @param integer $maxAge 最长存活期
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function cache($engine = "file", $ttl = 3600, $maxAge = 0) {
		$this->cache = array(
			"engine" => $engine,
			"ttl" => $ttl,
			"maxAge" => $maxAge
		);
		return $this;
	}	
	
	/**
	 * 当前cache的键值
	 * 
	 * 和cache()配合使用
	 *
	 * @param string $key 键值
	 * @return IDbPartitionQuery
	 * @since 1.0.2
	 */
	function cacheKey($key) {
		$this->cache["key"] = $key;
		return $this;
	}
	
	/**
	 * 刷新
	 *
	 * @param string $engine 缓存引擎：file,apc
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function flush($engine = "file") {
		$this->flush = array("engine" => $engine);	
		return $this;
	}	
	
	private function _cacheKey($sql) {
		return isset($this->cache["key"]) ? $this->cache["key"] :
			"SQL:" . $this->dao->dbOptName() . ";" . preg_replace("/(\w+)" . $this->seperator . "\w+/", "\\1", $sql);
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $sql
	 * @return boolean
	 * @since 1.0
	 */
	private function flushCache($sql) {
		if (!empty($this->flush)) {
			$engine = ICache::engine($this->flush["engine"]);
			$deleted = $engine->delete($this->_cacheKey($sql));
			return $deleted;
		}
		return true;
	}

	/**
	 * Enter description here...
	 *
	 * @param unknown_type $sql
	 * @return unknown
	 * @since 1.0
	 */
	private function readCache($sql) {
		//检查缓存
		if (!empty($this->cache)) {
			$engine = ICache::engine($this->cache["engine"]);
			$value = $engine->value($this->_cacheKey($sql));
			if (!is_null($value)) {
				return $value;
			}
		}
		
		return null;
	}

	/**
	 * Enter description here...
	 *
	 * @param unknown_type $sql
	 * @param unknown_type $value
	 * @since 1.0
	 */
	private function writeCache($sql, $value) {
		//写入缓存
		if (!empty($this->cache)) {
			$engine = ICache::engine($this->cache["engine"]);
			$engine->write($this->_cacheKey($sql), $value, $this->cache["ttl"], $this->cache["maxAge"]);
		}
	}

	/**
	 * 设定要查询的SQL
	 * 
	 * 此时，只有params参数才有效
	 * 
	 * SQL中可以使用：
	 * - table.self 表示当前表
	 * - table.xxx 表示关联表
	 * 
	 * <code>
	 * $array = q("TblLog")->sql("SELECT self.*,category.name FROM table.self LEFT JOIN table.category on self.category_id=category.id WHERE self.id=4")->findAll();
	 * </code>
	 *
	 * @param string $nativeSql 原生的SQL
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function sql($nativeSql) {
		$this->nativeSql = $nativeSql;
		$this->queryType("sql");
		return $this;
	}	
	
	/**
	 * 设置数据表名称
	 *
	 * @param string $table 数据表名称
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function table($table) {
		$this->dao->setTable($table);
		return $this;
	}

	/**
	 * 加入between条件
	 *
	 * @param string $attr 属性名
	 * @param scalar $value1 值1
	 * @param scalar $value2 值2
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function between($attr, $value1, $value2) {
		if (is_numeric($value1) && is_numeric($value2)) {
			$min = min($value1, $value2);
			$value2 = max($value1, $value2);
			$value1 = $min;
		}
		$attr = $this->_fullField($attr);
		$this->cond($attr . " BETWEEN " . $this->_qData($value1) . " AND " . $this->_qData($value2));
		return $this;
	}	
	
	/**
	 * 加入like条件
	 *
	 * @param string $attr 属性名
	 * @param string $expr 表达式
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */
	function like($attr, $expr) {
		$attr = $this->_fullField($attr);
		$this->cond($attr . " LIKE " . $this->_qData($expr));
		return $this;
	}		

	/**
	 * 加入一组like条件，支持多关键字
	 *
	 * @param array $arr 属性名数组
	 * @param string $expr 表达式
	 * @param boolean $and 是否在子查询中使用and条件，默认使用
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */	
	public function likeAll(array $arr, $expr, $and=true, $match_mode=self::MATCH_MODE_ANYWHERE){
		if(empty($arr) || !is_array($arr) || count($arr)==0 || strlen(trim($expr))==0){
			return $this;
		}
		$keywords = preg_split("/\s+/", $expr, -1, PREG_SPLIT_NO_EMPTY);
		if(empty($keywords) || count($keywords)==0){
			return $this;
		}
		$keys = array();
		foreach($keywords as $keyword){
			$key = addslashes($keyword);
			switch ($match_mode){
				case self::MATCH_MODE_START :
       				$key="'{$key}%'";		
       				break;
				case self::MATCH_MODE_END :
       				$key="'%{$key}'";		
       				break;
				case self::MATCH_MODE_EXACT :
       				$key="'{$key}'";		
       				break;
				default:
       				$key="'%{$key}%'";			       				
			}		
			$keys[] = $key;
		}	
		$conds=array();
		$relation=$and==true?"and ":"or ";		
		foreach($arr as $key => $value){
			$field = $this->_fullField($value);
			$cond=" ( ";
			foreach($keys as $key){
				$cond = $cond." {$field} LIKE {$key} {$relation}";
			}
			$cond = substr($cond, 0, strlen($cond)-strlen($relation));
			$cond = $cond." ) ";
			$conds[] = $cond;
		}
		$conds = implode(" or ", $conds);
		$this->cond($conds);
		return $this;
	}	
	
	private function _dealAttrs($attrs) {
		if (is_string($attrs)) {
			if (!strstr($attrs, ".")) {
				$attrs = "self." . $attrs;
			}
		}
		else if (is_array($attrs)) {
			$newAttrs = array();
			foreach ($attrs as $attr => $value) {
				if (!strstr($attr, ".")) {
					$newAttrs["self." . $attr] = $value;
				}
				else {
					$newAttrs[$attr] = $value;
				}
			}
			$attrs = $newAttrs;
		}
		return $attrs;
	}	
	
	/**
	 * 设置连接的属性
	 *
	 * @param string|array $attr 属性或多个属性
	 * @param string $type 连接类型，如 LEFT/RIGHT/INNER
	 * @param string|null $on 连接字段
	 * @return IDbPartitionQuery
	 * @since 1.0
	 */	
	function join($attr, $type = "LEFT") {
		if (!is_array($attr)) {
			$attr = array($attr => strtoupper($type));
		}
		$this->joinTypes = array_merge($this->joinTypes, $attr);
		return $this;
	}
	
	/**
	 * 取出前N条数据
	 *
	 * @param integer|null $size 条数
	 * @param string|null $cond 条件
	 * @param string|null $order 排序，如 id desc
	 * @param boolean $retArray 是否返回数组，如果为false，则返回IDbPartitionRecordSet
	 * @return array|IDbPartitionRecordSet
	 * @since 1.0
	 */
	function top($size = null, $cond = null, $order = null, $retArray = true) {
		$query = clone $this;
		if ($cond) {
			$query->cond($cond);
		}
		if ($order) {
			$query->order($order);
		}
		if ($size) {
			$query->size($size);
		}
		return $query->findAll($retArray);
	}
	
	/**
	 * 查询分页
	 *
	 * @param string $style 分页样式
	 * @param array $params 分页样式的参数
	 * @return IPageStyle
	 * @since 1.0.2
	 */
	function page($style = "yahoo", array $params = array()) {
		$pager = if_is_instance_of($style, "IPageStyle") ? $style : IPager::style($style, $params);
		return $this->queryPage($pager, isset($params["array"]) ? $params["array"] : true);
	}
	
	/**
	 * 是否在find/findAll等查询时是否检验记录集是否为空
	 * 
	 * 如果为空，则抛出一个异常
	 *
	 * @param boolean $assert 是否assert
	 * @param string $message 消息
	 * @return IDbPartitionQuery
	 * @since 1.0.2
	 */
	function assert($assert = true, $message = null) {
		$this->assert = $assert ? array("message" => $message) : array();
		return $this;
	}
	
	/**
	 * 序列化回调函数
	 *
	 * @return array
	 * @since 1.0.3
	 */
	function __sleep() {
		$attrs = array_keys(get_object_vars($this));
		$attrs = array_diff($attrs, array("dao", "driver"));
		return $attrs;
	}
	
	/**
	 * 反序列化回调函数
	 *
	 * @since 1.0.3
	 */
	function __wakeup() {
		$this->__construct($this->type);
	}
}


?>